package com.bfly.mall.admin.controller;

import com.bfly.common.ResponseUtil;
import com.bfly.common.ipseek.IPLocation;
import com.bfly.common.ipseek.IpSeekerUtil;
import com.bfly.common.page.Pager;
import com.bfly.common.util.JwtUtil;
import com.bfly.core.base.controller.BaseController;
import com.bfly.core.base.entity.ResponseData;
import com.bfly.core.cache.AdminMenuCacheUtil;
import com.bfly.core.context.ContextUtil;
import com.bfly.core.context.IpThreadLocal;
import com.bfly.core.context.PagerThreadLocal;
import com.bfly.core.context.event.AdminMenuReloadEvent;
import com.bfly.core.enums.SysError;
import com.bfly.core.exception.ServiceResponseException;
import com.bfly.core.security.*;
import com.bfly.mall.admin.dto.*;
import com.bfly.mall.admin.entity.Admin;
import com.bfly.mall.admin.entity.AdminLoginLog;
import com.bfly.mall.admin.entity.Role;
import com.bfly.mall.admin.service.IAdminLoginLogService;
import com.bfly.mall.admin.service.IAdminService;
import com.bfly.mall.admin.service.IMenuService;
import com.octo.captcha.service.CaptchaService;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.util.Assert;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 系统用户
 *
 * @author 长沙商脉科技有限公司
 * @date 2020/9/16 13:28
 */
@RestController
@RequestMapping(value = "/manage/admin")
public class AdminController extends BaseController {

    private Logger logger = LoggerFactory.getLogger(AdminController.class);

    @Autowired
    private IAdminService adminService;
    @Autowired
    private IAdminLoginLogService loginLogService;
    @Autowired
    private IMenuService menuService;
    @Autowired
    private CaptchaService captchaService;
    @Autowired
    private ApplicationContext context;

    /**
     * 管理员登录
     *
     * @author 长沙商脉科技有限公司
     * @date 2020/9/16 13:30
     */
    @PostMapping(value = "/login")
    @Login(required = false)
    public void login(HttpServletResponse response, HttpServletRequest request, @Valid @RequestBody AdminLoginDTO loginDTO, BindingResult result) {
        validData(result);
        boolean valid = captchaService.validateResponseForID(request.getSession().getId(), loginDTO.getCaptcha());
        Assert.isTrue(valid, "验证码不正确!");

        AdminDTO dto = new AdminDTO();
        Admin admin = adminService.getAdmin(loginDTO.getUserName());
        PwdEncoder pwdEncoder = new Md5PwdEncoder();
        if (admin == null || !pwdEncoder.isPasswordValid(admin.getPassword(), loginDTO.getPassword())) {
            //抛出没找到账号异常,密码错误异常
            throw new ServiceResponseException(SysError.PARAM_ERROR, "账号或密码错误!");
        }

        if (!admin.isEnable()) {
            //抛出账号被锁定异常
            throw new ServiceResponseException(SysError.PARAM_ERROR, "账号已被锁定，请联系管理员进行处理！");
        }

        // token 有效期2小时
        String token = JwtUtil.generateTokenForAdmin(admin.getId(), admin.getUserName());
        dto.setToken(token);

        //修改最后一次登录的时间
        admin.setLastLoginIp(IpThreadLocal.get());
        admin.setLastLoginDate(new Date());
        BeanUtils.copyProperties(admin, dto);

        adminService.edit(admin);

        loginLog(admin.getUserName(), true, request);
        loadAdminMenuToCache(admin);
        ResponseUtil.writeJson(response, ResponseData.getSuccess(dto));
    }

    /**
     * 加载管理员权限到缓存
     *
     * @author 长沙商脉科技有限公司
     * @date 2020/10/16 14:01
     */
    private void loadAdminMenuToCache(Admin admin) {
        context.publishEvent(new AdminMenuReloadEvent(context, admin.getUserName()));
    }

    /**
     * 管理员登出
     *
     * @author 长沙商脉科技有限公司
     * @date 2020/9/16 18:18
     */
    @GetMapping(value = "/logout")
    @PermissionCheck(required = false)
    public void logOut(HttpServletResponse response, HttpServletRequest request) {
        Admin admin = getAdmin();

        // 清空用户权限缓存
        AdminMenuCacheUtil.clear(admin.getUserName());

        loginLog(admin.getUserName(), false, request);
        logger.info(String.format("%s 安全退出,权限清除成功!", admin.getUserName()));
        ResponseUtil.writeJson(response, ResponseData.getSuccess(""));
    }

    /**
     * 记录登录登出日志
     *
     * @author 长沙商脉科技有限公司
     * @date 2020/12/24 11:10
     */
    private void loginLog(String userName, boolean isLogin, HttpServletRequest request) {
        IPLocation location = IpSeekerUtil.getIPLocation(IpThreadLocal.get());
        String agent = request.getHeader("User-Agent");
        AdminLoginLog loginLog = new AdminLoginLog();
        loginLog.setIp(IpThreadLocal.get());
        loginLog.setLogin(isLogin);
        loginLog.setUserName(userName);
        loginLog.setCreateDate(new Date());
        if (location != null) {
            loginLog.setCity(location.getArea());
            loginLog.setCountry(location.getCountry());
        }
        loginLog.setUserAgent(agent);
        loginLogService.save(loginLog);
    }

    /**
     * 获得当前登录管理员信息
     *
     * @author 长沙商脉科技有限公司
     * @date 2020/9/16 16:41
     */
    @GetMapping(value = "/currentInfo")
    @PermissionCheck(required = false)
    public void getCurrentAdminInfo(HttpServletResponse response) {
        Admin admin = ContextUtil.getLoginAdmin();
        AdminDTO dto = new AdminDTO();
        BeanUtils.copyProperties(admin, dto);
        dto.setPerms(null);
        ResponseUtil.writeJson(response, ResponseData.getSuccess(dto));
    }

    /**
     * 获得当前登录用户的权限集合
     *
     * @author 长沙商脉科技有限公司
     * @date 2020/10/16 10:57
     */
    @GetMapping(value = "/permissions")
    @PermissionCheck(required = false)
    public void getCurrentAdminPermission(HttpServletResponse response) {
        Admin admin = getAdmin();
        List<MenuListDTO> list = menuService.getFunctionMenuByAdmin(admin.getId());
        ResponseUtil.writeJson(response, ResponseData.getSuccess(list));
    }

    /**
     * 管理员列表
     *
     * @author 长沙商脉科技有限公司
     * @date 2020/9/16 18:51
     */
    @GetMapping(value = "/list")
    public void list(HttpServletResponse response, HttpServletRequest request) {
        PagerThreadLocal.set(request);
        String userName = request.getParameter("username");
        Map<String, String> unExactQuery = new HashMap<>(2);
        if (StringUtils.isNotBlank(userName)) {
            unExactQuery.put("user_name", userName);
        }
        Pager<Admin> pager = adminService.getPage(null, unExactQuery, null);
        Pager<AdminListDTO> dtoPager = new Pager<>(pager.getPageNo(), pager.getPageSize(), pager.getTotalCount());

        if (pager.getData() != null) {
            List<AdminListDTO> list = new ArrayList<>();
            for (Admin admin : pager.getData()) {
                AdminListDTO dto = new AdminListDTO();
                BeanUtils.copyProperties(admin, dto);
                dto.setRoles(admin.getRoles().stream().map(Role::getName).collect(Collectors.toList()));
                dto.setRoleIds(admin.getRoles().stream().map(Role::getId).collect(Collectors.toList()));
                list.add(dto);
            }
            dtoPager.setData(list);
        }
        ResponseUtil.writeJson(response, ResponseData.getSuccess(dtoPager));
    }

    /**
     * 新增管理员
     *
     * @author 长沙商脉科技有限公司
     * @date 2020/9/17 13:11
     */
    @PostMapping(value = "/add")
    @ActionModel(value = "新增管理员")
    public void save(HttpServletResponse response, @Valid @RequestBody AdminFormDTO formDTO, BindingResult result) {
        validData(result);
        adminService.save(formDTO);
        ResponseUtil.writeJson(response, ResponseData.getSuccess(""));
    }

    /**
     * 编辑管理员信息
     *
     * @author 长沙商脉科技有限公司
     * @date 2020/9/17 13:26
     */
    @PostMapping(value = "/edit")
    @ActionModel(value = "编辑管理员")
    public void edit(HttpServletResponse response, @Valid @RequestBody AdminFormDTO formDTO, BindingResult result) {
        validData(result);
        adminService.edit(formDTO);
        ResponseUtil.writeJson(response, ResponseData.getSuccess(""));
    }

    /**
     * 删除管理员信息
     *
     * @author 长沙商脉科技有限公司
     * @date 2020/9/17 13:27
     */
    @PostMapping(value = "/delete")
    @ActionModel(value = "删除管理员")
    public void delete(HttpServletResponse response, @RequestBody Integer... adminIds) {
        Admin admin=getAdmin();
        for (Integer id : adminIds) {
            if(admin.getId()==id.intValue()){
                throw new ServiceResponseException(SysError.PARAM_ERROR,"不能删除当前登录用户!");
            }
        }
        adminService.remove(adminIds);
        ResponseUtil.writeJson(response, ResponseData.getSuccess(""));
    }

    /**
     * 修改密码
     *
     * @author 长沙商脉科技有限公司
     * @date 2020/9/17 15:26
     */
    @PostMapping(value = "/edit/pwd")
    @ActionModel(value = "修改密码")
    public void editPwd(HttpServletResponse response, @Valid @RequestBody AdminEditPwdDTO pwdDTO, BindingResult result) {
        validData(result);
        Assert.isTrue(pwdDTO.getNewPassword().equalsIgnoreCase(pwdDTO.getConfirmPassword()), "两次密码不一致!");

        pwdDTO.setUserName(getAdmin().getUserName());
        adminService.editPwd(pwdDTO.getUserName(), pwdDTO.getPassword(), pwdDTO.getNewPassword());
        ResponseUtil.writeJson(response, ResponseData.getSuccess(""));
    }

    /**
     * 管理员绑定微信通知
     *
     * @author 长沙商脉科技有限公司
     * @date 2020/9/17 21:16
     */
    @GetMapping(value = "/bind/notify")
    @ActionModel(value = "绑定管理员通知")
    public void bindAdminNotify(HttpServletResponse response) {
        adminService.bindAdminNotify(getAdmin().getUserName());
        ResponseUtil.writeJson(response, ResponseData.getSuccess(""));
    }
}
